package com.libreaccess.reporting.scheduler;

import java.sql.SQLException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;

import javax.annotation.Resource;
import javax.naming.NamingException;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

import com.libreaccess.reporting.beans.ReportsDAO;
import com.libreaccess.reporting.beans.ReportsDAOImpl;
import com.libreaccess.reporting.utils.Logger;
import com.libreaccess.reporting.utils.ReportUtil;
import com.libreaccess.reporting.utils.JasperUtils;

public class SchedulerManager {
	public static Scheduler scheduler;
	public static StdSchedulerFactory factory;
	public static SchedulerManager instance;
	public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY";

	public static final String JOB_GROUP = "Job Group";
	public static final String TRIGGER_GROUP = "Trigger Group";

	final static Logger logger = new Logger(SchedulerManager.class);

	@Resource
	private static ReportsDAO rDao;

	public void setrDao(ReportsDAO rDao) {
		SchedulerManager.rDao = rDao;
	}
	
	/*
	 * this method initializes Scheduler Manager
	 * @return void
	 */
	public static void init() {
		if (instance == null) {
			instance = new SchedulerManager();
		}
		logger.debug("Scheduler has been started...");
	}

	public SchedulerManager() {
		try {
			if (factory == null) {
				factory = new StdSchedulerFactory("quartz.properties");
			}
			scheduler = factory.getScheduler();
			if (!scheduler.isStarted())
				scheduler.start();
		} catch (SchedulerException ex) {
			logger.error("SchedulerException:"+ex.getMessage());
		}
	}

	/*
	 * this method adds job for quartz trigger
	 * @param ReportUtil reportData
	 * @param boolean isTriggerCreated
	 * @return void
	 */
	public static void addJob(ReportUtil reportData, boolean isTriggerCreated) {
		try {
			if (rDao == null)
			{
				rDao = new ReportsDAOImpl();
			}
			if (/*reportData.getSchedulerCountToRun() > 0*/ isTriggerCreated || getTrigger(reportData.getTriggerId()).getNextFireTime() != null)
			{
				JobDetail jobDetail = new JobDetail(reportData.getTriggerId(), JOB_GROUP, SchedulerJob.class);
				jobDetail.getJobDataMap().put("parameters", reportData);
				jobDetail.setDurability(true);
				scheduler.addJob(jobDetail, true);
				
				
				if (reportData.getTriggerId() != null && reportData.getTriggerExpr() != null && (getTrigger(reportData.getTriggerId()) != null)) {
					reportData.getTriggerExpr().setNextFireTime(getTrigger(reportData.getTriggerId()).getNextFireTime());	
					logger.debug("!!!!!!!!!Next Fire Time for Trigger:"+reportData.getTriggerExpr().getNextFireTime());
				}
			}
			else
			{
				logger.debug("Trigger "+ reportData.getTriggerId() + " was removed");
				deleteJobAndTrigger(reportData.getTriggerId());
				reportData.setTriggerId(null);
				
				reportData.clearSchedulerData();
			}
			/*some log data */
/*			logger.debug("We save some data:");
			logger.debug("Format:"+reportData.getReportFormat());*/
			
			rDao.updateMyReportByTrigger(reportData.getMyReportId(), JasperUtils.putReportUtilToArray(reportData));
			logger.debug("Data in the DB for myReport:"+reportData.getMyReportId() +" was updated");
		}
		catch (SchedulerException e) {
			logger.error("SchedulerException:"+e.getMessage());
		} catch (NamingException e) {
			logger.error("NamingException:"+e.getMessage());
		} catch (SQLException e) {
			logger.error("SQLException:"+e.getMessage());
		}
	}

	/*
	 * this method creates a trigger
	 * @param ReportUtil reportData
	 * @param boolean isTriggerCreated
	 * @return void
	 */
	public static void addTrigger(ReportUtil reportData) {
		try {
			if (rDao == null)
			{
				rDao = new ReportsDAOImpl();
			}
			Calendar cl = Calendar.getInstance();
			Date startDate = reportData.getSchedulerStartDate();
//			if (reportData.getSchedulerFrequency() != Utils.SchedulerFrequency.OneTime) {
//				cl.setTime(startDate);
//				cl.add(Calendar.HOUR, 24); //add 1 day
//				cl.set(Calendar.HOUR, 0);
//				cl.set(Calendar.MINUTE, 0);
//				cl.set(Calendar.SECOND, 0);
				startDate = cl.getTime();
//			}
			cl.set(2100, 1, 1);
			CronTrigger trig = new CronTrigger(reportData.getTriggerId(), TRIGGER_GROUP, reportData.getTriggerId(), JOB_GROUP, startDate, cl.getTime(),  reportData.getTriggerExpr().getExpression());
			trig.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_SMART_POLICY );
			scheduler.scheduleJob(trig);
			logger.debug("Trigger:"+reportData.getTriggerId()+" is started");
			scheduler.start();
			
			reportData.getTriggerExpr().setNextFireTime(trig.getNextFireTime());	
			
			logger.debug("!!!!!!!!!Next Fire Time for Trigger:"+reportData.getTriggerExpr().getNextFireTime());
			
			rDao.updateMyReportByTrigger(reportData.getMyReportId(), JasperUtils.putReportUtilToArray(reportData));
			logger.debug("Data in the DB for myReport:"+reportData.getMyReportId() +" was updated");
		}
		catch (SchedulerException e) {
			logger.error("SchedulerException:"+e.getMessage());
		}
		catch (ParseException e) {
			logger.error("ParseException:"+e.getMessage());
		} catch (NamingException e) {
			logger.error("NamingException:"+e.getMessage());
		} catch (SQLException e) {
			logger.error("SchedulerException:"+e.getMessage());
		}
	}
	
	/*
	 * this method creates a trigger and add job to it
	 * @param ReportUtil reportData
	 * @return void
	 */
	public static void addJobAndTrigger(ReportUtil reportData) {
		deleteJobAndTrigger(reportData.getTriggerId()); // delete job and trigger if they are exists 
			
		if (reportData.getSchedulerCountToRun() > 0)
		{
			addJob(reportData, true);
			addTrigger(reportData);
		}
		else
		{
			logger.debug("Trigger "+ reportData.getTriggerId() + " was expired");
		}
	}

	/*
	 * this method get trigger by his id
	 * @param String id
	 * @return Trigger
	 */
	public static Trigger getTrigger(String id) {
		Trigger t = null;

		try {
			t =	scheduler.getTrigger(id, TRIGGER_GROUP);
		} catch (SchedulerException e) {
			logger.error("SchedulerException:"+e.getMessage());
		}
		return t;
	}

	/*
	 * this method deletes job and trigger
	 * @param String id
	 * @return void
	 */
	public static void deleteJobAndTrigger(String id) {
		deleteTrigger(id);
		deleteJob(id);
	}

	/*
	 * this method deletes job
	 * @param String id
	 * @return void
	 */
	public static void deleteJob(String id) {
		try {
			if (scheduler.getJobDetail(id, JOB_GROUP) != null) {
				scheduler.deleteJob(id, JOB_GROUP);
				logger.debug("Triger id:" + id + " JOB was removed");
			}
		} catch (SchedulerException ex) {
			logger.error("SchedulerException:"+ex.getMessage());
		}
	}
	/*
	 * this method deletes job
	 * @param String id
	 * @return void
	 */	
	public static void deleteTrigger(String id) {
		try {
			if (scheduler.getTrigger(id, TRIGGER_GROUP) != null) {
				scheduler.unscheduleJob(id, TRIGGER_GROUP);
				logger.debug("Triger id:" + id + " Trigger was removed");
			}
		} catch (SchedulerException ex) {
			logger.error(ex.getMessage());
		}
	}
}
